/*********************************************************************
 *                                                                   *
 *  Make Directory Difference ( MakeDirDiff )                        *
 *  Author: Igor Kanshyn                                             *
 *  Copyright (c) 2009, Igor Kanshyn. All rights reserved.           *
 *                                                                   *
 *  MakeDirDiff is released under the Apache Software License.       *
 *  See LICENSE.txt for more details.                                *
 *                                                                   *
 *********************************************************************/
package org.grenader.makedirdiff;

import org.junit.Test;
import org.junit.Ignore;
import org.junit.Before;
import static org.junit.Assert.*;
import static org.junit.Assert.assertFalse;
import org.grenader.jscssmin.files.FileUtils;
import org.grenader.jscssmin.TmpDirTestCaseBase;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.Project;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;

/**
 * <p>Product: MakeDirDiff ( Make Directory Difference )<br> Author: Igor Kanshyn (grenader). </p>
 * Date: Sep 11, 2009
 */
public class MakeDirDiffTestCase extends TmpDirTestCaseBase {
  private MakeDirDiff diff = new MakeDirDiff();
  private String helpText = MakeDirDiff.HELP_TEXT;

  @Before
  public void setUp() throws IOException {
    super.setUp();
    diff.setProject(new Project());

    FileSet fileSet = new FileSet();
    fileSet.setDir(tmpOriginalDir);
    diff.addFileset(fileSet);
  }

  @Test @Ignore
  public void testGetFileListing() throws IOException {
    FileSet fileSet = new FileSet();
    fileSet.setDir(new File("c:/DD"));
    diff.addFileset(fileSet);

    diff.setDestDir(new File("c:/DD_NEW"));
    diff.setPrevDir(new File("c:/DD_OLD"));
    diff.execute();

    for (File file : diff.getNewFiles()) {
      System.out.println("new: " + file.getAbsolutePath());
    }
  }

  @Test
  public void testExecute_justNewFiles() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.execute();

    //check previous dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "22.txt")));

    //check result dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "22.txt")));
    assertEquals(2, diff.getNewFiles().size());
  }

  @Test
  public void testMain_notEnoughParams() throws IOException {
    SystemStreamsController streamsController = new SystemStreamsController();

    MakeDirDiff.main();
    streamsController.restoreSystemStreams();

    assertEquals("MakeDirDiff requires at least three command line paraments.\n" + helpText + "\n",
                 streamsController.getErrStream());
    assertEquals("", streamsController.getOutStream());


    streamsController = new SystemStreamsController();
    MakeDirDiff.main("/tmp");
    streamsController.restoreSystemStreams();

    assertEquals("MakeDirDiff requires at least three command line paraments.\n" + helpText + "\n",
                 streamsController.getErrStream());
    assertEquals("", streamsController.getOutStream());


    streamsController = new SystemStreamsController();
    MakeDirDiff.main("/tmp", "/temp");
    streamsController.restoreSystemStreams();

    assertEquals("MakeDirDiff requires at least three command line paraments.\n" + helpText + "\n",
                 streamsController.getErrStream());
    assertEquals("", streamsController.getOutStream());
  }

  @Test
  public void testMain_wrongOriginalDir() throws IOException {
    SystemStreamsController streamsController = new SystemStreamsController();

    String nonExistingDir = Math.round(Math.random() * 1000000) + "-dir";

    try {
      MakeDirDiff.main(nonExistingDir, "/tmp", "/temp");
    }
    catch (Exception e) {
      e.printStackTrace();
    }

    streamsController.restoreSystemStreams();

    assertTrue("Error message is not correct.", streamsController.getErrStream().contains("java.lang.IllegalArgumentException: Original directory does not exist: "+nonExistingDir));
    assertEquals("", streamsController.getOutStream());
  }


  @Test
  public void testMain_justNewFiles_andCleaningDestDir() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22.txt"));

    MakeDirDiff.main(tmpOriginalDir.getAbsolutePath(), tmpDestinatonDir.getAbsolutePath(),
                     tmpAdditionalDir.getAbsolutePath());

    //check previous dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "22.txt")));

    //check result dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "22.txt")));

    // Second call.
    // Destination directory should be clean.
    // No new files should be found
    MakeDirDiff.main("-cleanDestDir", tmpOriginalDir.getAbsolutePath(), tmpDestinatonDir.getAbsolutePath(),
                     tmpAdditionalDir.getAbsolutePath());

    //check result dir
    assertFalse(new File(tmpDestinatonDir, "11.txt").exists());
    assertFalse(new File(tmpDestinatonDir, "22.txt").exists());
  }

  @Test
  public void testExecute_newFilesInSubDirs() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11-root.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22-root.txt"));

    File dir1 = new File(tmpOriginalDir, "dir1");
    dir1.mkdirs();
    FileUtils.copyFile(file1, new File(dir1, "11.txt"));

    File dir2 = new File(tmpOriginalDir, "dir2");
    dir2.mkdirs();
    FileUtils.copyFile(file2, new File(dir2, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.execute();

    //check previous dir
    assertEquals("11\n11\n",
                 FileUtils.readFileContent(new File(tmpAdditionalDir + "/dir1", "11.txt")));
    assertEquals("22\n22\n22\n",
                 FileUtils.readFileContent(new File(tmpAdditionalDir + "/dir2", "22.txt")));

    //check result dir
    assertEquals("11\n11\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir1", "11.txt")));
    assertEquals("22\n22\n22\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir2", "22.txt")));
    assertEquals(4, diff.getNewFiles().size());
  }

  @Test
  public void testExecute_newFilesInSubDirs_twoCalls() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11-root.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22-root.txt"));

    File dir1 = new File(tmpOriginalDir, "dir1");
    dir1.mkdirs();
    FileUtils.copyFile(file1, new File(dir1, "11.txt"));

    File dir2 = new File(tmpOriginalDir, "dir2");
    dir2.mkdirs();
    FileUtils.copyFile(file2, new File(dir2, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.execute();

    //check previous dir
    assertEquals("11\n11\n",
                 FileUtils.readFileContent(new File(tmpAdditionalDir + "/dir1", "11.txt")));
    assertEquals("22\n22\n22\n",
                 FileUtils.readFileContent(new File(tmpAdditionalDir + "/dir2", "22.txt")));

    //check result dir
    assertEquals("11\n11\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir1", "11.txt")));
    assertEquals("22\n22\n22\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir2", "22.txt")));
    assertEquals(4, diff.getNewFiles().size());

    // Second Time

    // clear result dir.
    FileUtils.deleteDirectory(tmpDestinatonDir);
    diff.execute();

    assertEquals(0, diff.getNewFiles().size());
  }

  @Test
  public void testExecute_noKeepOldFiles_twoCalls() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11-root.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22-root.txt"));

    File dir1 = new File(tmpOriginalDir, "dir1");
    dir1.mkdirs();
    FileUtils.copyFile(file1, new File(dir1, "11.txt"));

    File dir2 = new File(tmpOriginalDir, "dir2");
    dir2.mkdirs();
    FileUtils.copyFile(file2, new File(dir2, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.setProcessPreviousDir(false);
    diff.execute();

    //check previous dir
    assertFalse(new File(tmpAdditionalDir + "/dir1", "11.txt").exists());
    assertFalse(new File(tmpAdditionalDir + "/dir2", "22.txt").exists());

    //check result dir
    assertEquals("11\n11\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir1", "11.txt")));
    assertEquals("22\n22\n22\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir2", "22.txt")));
    assertEquals(4, diff.getNewFiles().size());

    // Second Time

    // clear result dir.
    FileUtils.deleteDirectory(tmpDestinatonDir);
    diff.setProcessPreviousDir(true);
    diff.execute();

    assertEquals(4, diff.getNewFiles().size());
  }

  @Test
  public void testExecute_cleanDestDir_twoCalls() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11-root.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22-root.txt"));

    File dir1 = new File(tmpOriginalDir, "dir1");
    dir1.mkdirs();
    FileUtils.copyFile(file1, new File(dir1, "11.txt"));

    File dir2 = new File(tmpOriginalDir, "dir2");
    dir2.mkdirs();
    FileUtils.copyFile(file2, new File(dir2, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.setCleanDestDir(true);
    diff.execute();

    //check result dir
    assertEquals("11\n11\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir1", "11.txt")));
    assertEquals("22\n22\n22\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir2", "22.txt")));
    assertEquals(4, diff.getNewFiles().size());


    // Second Time

    // We should not clear result dir. The system do this for us
    diff.setProcessPreviousDir(true);
    diff.execute();

    //check result dir again. It should not have amy files
    assertFalse(new File(tmpDestinatonDir + "/dir1", "11.txt").exists());
    assertFalse(new File(tmpDestinatonDir + "/dir2", "22.txt").exists());
  }

  @Test
  public void testExecute_newFilesInSubDirsSecondLevel() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11-root.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22-root.txt"));

    File dir1 = new File(tmpOriginalDir, "dir1");
    dir1.mkdirs();
    FileUtils.copyFile(file1, new File(dir1, "11.txt"));
    File dir11 = new File(dir1, "dir11");
    dir11.mkdirs();
    FileUtils.copyFile(file1, new File(dir11, "11-second.txt"));

    File dir2 = new File(tmpOriginalDir, "dir2");
    dir2.mkdirs();
    FileUtils.copyFile(file2, new File(dir2, "22.txt"));
    File dir21 = new File(dir2, "dir22");
    dir21.mkdirs();
    FileUtils.copyFile(file2, new File(dir21, "22-second.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.execute();

    //check previous dir
    assertEquals("11\n11\n",
                 FileUtils.readFileContent(new File(tmpAdditionalDir + "/dir1", "11.txt")));
    assertEquals("22\n22\n22\n",
                 FileUtils.readFileContent(new File(tmpAdditionalDir + "/dir2", "22.txt")));

    //check result dir
    assertEquals("11\n11\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir1", "11.txt")));
    assertEquals("22\n22\n22\n",
                 FileUtils.readFileContent(new File(tmpDestinatonDir + "/dir2", "22.txt")));

    assertEquals("22\n22\n22\n", FileUtils.readFileContent(
        new File(tmpDestinatonDir + "/dir2/dir22", "22-second.txt")));

    assertEquals(6, diff.getNewFiles().size());
  }

  @Test
  public void testExecute_twice_noChanges() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.execute();

    //check previous dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "22.txt")));

    //check result dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "22.txt")));
    assertEquals(2, diff.getNewFiles().size());

    // Second Time

    // clear result dir.
    FileUtils.deleteDirectory(tmpDestinatonDir);
    diff.execute();

    assertEquals(0, diff.getNewFiles().size());
    assertEquals(2, diff.getProcessedFilesCount());
  }

  @Test
  public void testExecute_twice_oneFileChanged() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.execute();

    //check previous dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "22.txt")));

    //check result dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "22.txt")));
    assertEquals(2, diff.getNewFiles().size());

    // Second Time
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "11.txt"));

    // clear result dir.
    FileUtils.deleteDirectory(tmpDestinatonDir);
    diff.execute();

    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "11.txt")));
    assertEquals(1, diff.getNewFiles().size());
  }

  @Test
  public void testExecute_twice_oneNewFile() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.execute();

    //check previous dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "22.txt")));

    //check result dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "22.txt")));
    assertEquals(2, diff.getNewFiles().size());

    // Second Time
    File file3 = new File(currentClassDirPath, "1.txt");
    FileUtils.copyFile(file3, new File(tmpOriginalDir, "33.txt"));

    // clear result dir.
    FileUtils.deleteDirectory(tmpDestinatonDir);
    diff.execute();

    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "33.txt")));
    assertEquals(1, diff.getNewFiles().size());
  }

  @Test
  public void testExecute_twice_oneNewFileDate() throws IOException {
    String currentClassDirPath = MakeDirDiffTestCase.class.getResource("").getFile();
    System.out.println("testFilePath = " + currentClassDirPath);

    File file1 = new File(currentClassDirPath, "1.txt");
    File file2 = new File(currentClassDirPath, "2.txt");
    FileUtils.copyFile(file1, new File(tmpOriginalDir, "11.txt"));
    FileUtils.copyFile(file2, new File(tmpOriginalDir, "22.txt"));

    diff.setDestDir(tmpDestinatonDir);
    diff.setPrevDir(tmpAdditionalDir);

    diff.execute();

    //check previous dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpAdditionalDir, "22.txt")));

    //check result dir
    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "11.txt")));
    assertEquals("22\n22\n22\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "22.txt")));
    assertEquals(2, diff.getNewFiles().size());

    // Second Time
    new File(tmpOriginalDir, "11.txt").setLastModified(System.currentTimeMillis());

    // clear result dir.
    FileUtils.deleteDirectory(tmpDestinatonDir);
    diff.execute();

    assertEquals("11\n11\n", FileUtils.readFileContent(new File(tmpDestinatonDir, "11.txt")));
    assertEquals(1, diff.getNewFiles().size());
  }

  @Test
  public void testValidateDirectory_exists() throws IOException {
    File dir = new File(tmpOriginalDir, "dir-type");
    dir.mkdirs();
    
    new MakeDirDiff().validateDirectory("type", dir, false);
  }

  @Test(expected = IllegalArgumentException.class)
  public void testValidateDirectory_file() throws IOException {
    File dir = new File(tmpOriginalDir, "dir-type");

    assertTrue(dir.createNewFile());

    new MakeDirDiff().validateDirectory("type", dir, false);
  }

  @Test(expected = IllegalArgumentException.class)
  public void testValidateDirectory_doesNotExists() throws IOException {
    File dir = new File(tmpOriginalDir, "dir-type");
    //  we are not creating it:  dir.mkdirs();

    new MakeDirDiff().validateDirectory("type", dir, false);
  }

  @Test(expected = IllegalArgumentException.class)
  public void testValidateDirectory_isNull() throws IOException {
    new MakeDirDiff().validateDirectory("type", null, false);
  }


}
